package xyz.xpman.prisonerfitness.activity;

import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.TimerTask;

import xyz.xpman.prisonerfitness.R;
import xyz.xpman.prisonerfitness.activity.base.BaseActivity;
import xyz.xpman.prisonerfitness.cache.AsyncImageLoader;
import xyz.xpman.prisonerfitness.config.AppConfig;
import xyz.xpman.prisonerfitness.config.Constant;
import xyz.xpman.prisonerfitness.entity.Mappings;
import xyz.xpman.prisonerfitness.entity.Moves;
import xyz.xpman.prisonerfitness.entity.TrainingAction;
import xyz.xpman.prisonerfitness.entity.TrainingStandard;
import xyz.xpman.prisonerfitness.kit.TimerKit;
import xyz.xpman.prisonerfitness.util.ImgUtil;
import xyz.xpman.prisonerfitness.view.CountdownRoundView;

public class TrainingActivity extends BaseActivity {
    private static final int CLOSE_CONFIRM_REQUEST_CODE = 0;

    private Moves move;
    private TrainingStandard.Standard standard;

    private ImageView start, pause, close;

    private ViewGroup groupRestTipWrap;
    private CountdownRoundView countdownRoundView;

    private ViewGroup trainingGuideImgWrap;
    private ImageView trainingGuideImg;

    private TextView startCountdown;

    private ViewGroup trainingTimesWrap;
    private TextView trainingFinishTimes;

    private ViewGroup trainingGroupsWrap;
    private TextView trainingFinishGroups;



    private TrainingStatus trainingStatus = TrainingStatus.STOPPED;

    private final int[] count = {3};

    private int usedTime = 0;
    private int trainingTimes = 0;
    private int trainingGroups = 1;


    private TimerTask groupTask = null;
    private TimerTask trainingTask = null;
    private TimerTask countdownTask = null;

    private MusicManager musicManager;
    private MusicPlayer musicPlayer;

    private final MediaPlayer trainingBgMusicPlayer = new MediaPlayer();

    private Bitmap[] actionImg;



    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        fullScreen();
        hideBottomUIMenu();
        hideTitleBar();

        setContentView(R.layout.activity_training);


        initData();
        initView();
        initEvent();

        prepareAndStart();
    }


    @RequiresApi(api = Build.VERSION_CODES.N)
    private void initData() {
        final String id = getIntentExtra(Constant.EXTRA_MOVES_ID);
        standard = getIntentExtra(Constant.EXTRA_STANDARD);

        move = Mappings.getMove(id);


        // 组间休息时间(百毫秒)
        int groupRestInterval = AppConfig.getGroupRestInterval();
        // 组消耗时间(百毫秒) = 起落时间 * 2 * 起落(整体)次数 + 休息时间
        int groupNeedTime = AppConfig.getUpAndDownInterval()
                * 2 * standard.getTimes() + groupRestInterval;

        // 耗时(秒): usedTime(百毫秒) / 10 == usedTime / 10
        // 耗时(分钟): usedTime(百毫秒) / 10 / 60 == usedTime / 600
        usedTime = (int)((groupNeedTime * standard.getGroups() - groupRestInterval) / 10f);
    }

    private void initView() {
        start = findViewById(R.id.start);
        pause = findViewById(R.id.pause);
        close = findViewById(R.id.close);

        startCountdown = findViewById(R.id.start_countdown);
        countdownRoundView = findViewById(R.id.countdownView);

        groupRestTipWrap = findViewById(R.id.group_rest_tip_wrap);

        trainingTimesWrap = findViewById(R.id.training_times_wrap);
        trainingFinishTimes = findViewById(R.id.training_finish_times);

        trainingGroupsWrap = findViewById(R.id.training_groups_wrap);
        trainingFinishGroups = findViewById(R.id.training_finish_groups);

        trainingGuideImgWrap = findViewById(R.id.training_action_guide_img_wrap);
        trainingGuideImg = findViewById(R.id.training_action_guide_img);



        ((TextView)findViewById(R.id.training_total_times))
                .setText(String.valueOf(standard.getTimes()));

        ((TextView)findViewById(R.id.training_total_groups))
                .setText(String.valueOf(standard.getGroups()));


        trainingFinishTimes.setText(String.valueOf(trainingTimes));
        trainingFinishGroups.setText(String.valueOf(trainingGroups));


        hideAll();
    }

    private void initEvent() {
        start.setOnClickListener(view -> {
            if (trainingStatus == TrainingStatus.COUNTDOWN_PAUSED) {
                continueCountdown();
            } else if (trainingStatus == TrainingStatus.PAUSED) {
                continueTraining();
            } else if (trainingStatus == TrainingStatus.REST_PAUSED) {
                continueRestCountdown();
            }
        });

        pause.setOnClickListener(view -> {
            if (trainingStatus == TrainingStatus.COUNTDOWN) {
                pauseCountdown();
            } else if (trainingStatus == TrainingStatus.STARTED) {
                pauseTraining();
            } else if (trainingStatus == TrainingStatus.REST) {
                pauseRestCountdown();
            }
        });

        close.setOnClickListener(view -> closeConfirm());
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private void prepareAndStart() {
        musicManager = new MusicManager(AppConfig.getMusicFileDir());
        musicPlayer = new MusicPlayer(
                musicManager.getNumOne(),
                musicManager.getNumTwo(),
                musicManager.getNumThree());

        initMediaPlayer();

        initActions(move.getTraining().getActions(), this::startTraining);
    }


    private void initActions(@NotNull final List<TrainingAction> actions,
                             @NotNull TimerKit.Task callback) {
        Log.i("TrainingAction", "actions: " + actions);

        if (actions.isEmpty()) {
            actionImg = new Bitmap[] {
                    ImgUtil.drawableToBitmap(getDrawable(R.drawable.abc_action_empty_guide_img)),
                    ImgUtil.drawableToBitmap(getDrawable(R.drawable.abc_action_empty_guide_img2))};
        } else {
            actionImg = new Bitmap[actions.size()];

            new Thread(() -> {
                for (int i = 0; i < actions.size(); i++) {
                    final TrainingAction action = actions.get(i);
                    Log.i("TrainingAction", "images: " + action.getImages());

                    actionImg[i] = AsyncImageLoader.load(action.getImages());

                    if (0 == i) {
                        runOnUiThread(() -> trainingGuideImg.setImageBitmap(actionImg[0]));
                    }
                }

                runOnUiThread(() -> callback.run());
            }).start();
        }
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



    private void startTraining() {
        showStartCountdown();
        trainingStatus = TrainingStatus.COUNTDOWN;

        trainingProcess();
    }

    private void trainingProcess() {
        startPlayer();

        showCountdown(() -> {
            hideStartCountdown();
            showForTraining();

            musicManager.trainingStart(() -> schedule(this::training));
        });
    }

    private void training() {
        final TimerKit.Task task = () -> trainingOneGroupTask(() -> {
            if (trainingGroups == standard.getGroups()) {
                groupTask.cancel();
                Log.i("training", "finish!");

                schedule(this::finishTraining);
                return;
            }

            trainingTimes = 0;
            trainingGroups++;

            runOnUiThread(() -> {
                final String temp = String.valueOf(trainingGroups);
                trainingFinishGroups.setText(temp);

                musicManager.trainingRecovery(() -> {
                    showForTrainingRest();
                    countdownRoundView.start();
                });
            });
        });


        // 这个和下面那个setCountdownFinishedListener可以合并起来
        countdownRoundView.setCountdownListener(num -> {
            if (num <= 3) {
                if (0 != num) {
                    musicPlayer.start(num - 1);
                } else {
                    musicManager.trainingStart(() -> {});
                }
            }
        });

        countdownRoundView.setCountdownFinishedListener(() -> {
            trainingStatus = TrainingStatus.STARTED;

            runOnUiThread(() -> {
                hideForTrainingRest();
                showForTraining();
            });

            groupTask = schedule(task);
        });
        // 跑一次
        countdownRoundView.requireCountdownFinished();
    }


    private void trainingOneGroupTask(@NotNull TimerKit.Task callback) {
        trainingTask = new TimerTask() {
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public void run() {
                int i = trainingTimes >> 1;

                int action = trainingTimes & 0b00000001;
                // up action
                if (0 == action) {
                    runOnUiThread(() ->
                            trainingFinishTimes.setText(String.valueOf(i)));
                }
                musicPlayer.start(action);

                runOnUiThread(() ->
                        trainingGuideImg.setImageBitmap(actionImg[action]));

                // 因为要完成所有，最后一次X/X也要现实
                if (i == standard.getTimes()) {
                    this.cancel();
                    callback.run();
                    return;
                }

                trainingTimes++;
            }
        };


        int upAndDownInterval = AppConfig.getUpAndDownInterval() * 100;
        TimerKit.DEFAULT.schedule(trainingTask, 0, upAndDownInterval);
    }



    private void pauseTraining() {
        showPauseForTraining();

        pausePlayer();

        groupTask.cancel();
        trainingTask.cancel();
        trainingStatus = TrainingStatus.PAUSED;
    }

    private void continueTraining() {
        showForTrainingPausedToContinue();

        startPlayer();

        countdownRoundView.requireCountdownFinished();
        trainingStatus = TrainingStatus.STARTED;
    }

    private void stopTraining() {
        if (null != groupTask) {
            groupTask.cancel();
        }

        if (null != trainingTask) {
            trainingTask.cancel();
        }
        trainingStatus = TrainingStatus.STOPPED;

        releasePlayer();
    }

    private void finishTraining() {
        stopTraining();

        musicManager.trainingFinish(() -> {
            Intent intent = newIntent(TrainingFinishActivity.class);

            intent.putExtra(Constant.EXTRA_MOVES_ID, move.getId());
            intent.putExtra(Constant.EXTRA_STANDARD, standard);
            intent.putExtra(Constant.EXTRA_USED_TIME, usedTime);

            startActivity(intent);
            finish();
        });
    }



    private void pauseRestCountdown() {
        showForTrainingRestPaused();

        pausePlayer();
        countdownRoundView.pause();
    }

    private void continueRestCountdown() {
        showForTrainingRestToContinue();

        startPlayer();
        countdownRoundView.resume();
    }



    private void pauseCountdown() {
        showPauseStartCountdown();

        pausePlayer();
        countdownTask.cancel();
    }


    private void continueCountdown() {
        showContinueStartCountdown();

        startPlayer();
        trainingProcess();
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private void showCountdown(@NotNull TimerKit.Task callback) {
        if (count[0] <= 0) {
            startCountdown.setVisibility(View.GONE);
            callback.run();
            return;
        }

        countdownTask = new TimerTask() {
            @Override
            public void run() {
                if (count[0] > 0) {
                    final String temp = String.valueOf(count[0]--);
                    musicPlayer.start(count[0]);

                    runOnUiThread(() -> startCountdown.setText(temp));
                } else {
                    runOnUiThread(() -> {
                        startCountdown.setVisibility(View.GONE);

                        callback.run();
                    });

                    cancel();
                }
            }
        };

        // 倒计时 3 --> 3秒 --> 间隔1秒
        TimerKit.DEFAULT.schedule(countdownTask, 0, 1000);
    }



    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



    private void hideAll() {
        groupRestTipWrap.setVisibility(View.GONE);
        start.setVisibility(View.GONE);

        hideStartCountdown();
        hideForTraining();
    }



    private void showStartCountdown() {
        startCountdown.setVisibility(View.VISIBLE);
    }

    private void hideStartCountdown() {
        startCountdown.setVisibility(View.GONE);
    }


    private void showPauseStartCountdown() {
        startCountdown.setVisibility(View.GONE);
        pause.setVisibility(View.GONE);

        start.setVisibility(View.VISIBLE);
    }

    private void showContinueStartCountdown() {
        startCountdown.setVisibility(View.VISIBLE);
        pause.setVisibility(View.VISIBLE);

        start.setVisibility(View.GONE);
    }



    private void showForTraining() {
        pause.setVisibility(View.VISIBLE);
        start.setVisibility(View.GONE);

        trainingTimesWrap.setVisibility(View.VISIBLE);
        trainingGroupsWrap.setVisibility(View.VISIBLE);

        trainingGuideImgWrap.setVisibility(View.VISIBLE);
    }

    private void hideForTraining() {
        trainingTimesWrap.setVisibility(View.GONE);
        trainingGroupsWrap.setVisibility(View.GONE);

        trainingGuideImgWrap.setVisibility(View.GONE);
    }



    private void showForTrainingRest() {
        hideForTraining();
        groupRestTipWrap.setVisibility(View.VISIBLE);
    }

    private void showForTrainingRestPaused() {
        groupRestTipWrap.setVisibility(View.GONE);
        pause.setVisibility(View.GONE);

        start.setVisibility(View.VISIBLE);
    }

    private void showForTrainingRestToContinue() {
        groupRestTipWrap.setVisibility(View.VISIBLE);
    }

    private void hideForTrainingRest() {
        showForTraining();
        groupRestTipWrap.setVisibility(View.GONE);
    }



    private void showPauseForTraining() {
        hideForTraining();
        start.setVisibility(View.VISIBLE);
        pause.setVisibility(View.GONE);
    }

    private void showForTrainingPausedToContinue() {
        showForTraining();
        start.setVisibility(View.GONE);
        pause.setVisibility(View.VISIBLE);
    }



    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private TimerTask schedule(@NotNull TimerKit.Task task) {
        return TimerKit.DEFAULT.schedule(task, 1000);
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private void initMediaPlayer() {
        try {
            // 指定音频文件路径
            trainingBgMusicPlayer.setDataSource(AppConfig.getDecodeBgMusicFileName());
            // 设置为循环播放
            trainingBgMusicPlayer.setLooping(true);
            // 初始化播放器MediaPlayer
            trainingBgMusicPlayer.prepare();

            final float volume = ((float) AppConfig.getVolume()) / 100f;
            Log.i("volume", "volume: " + volume);
            trainingBgMusicPlayer.setVolume(volume, volume);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void startPlayer() {
        if (!trainingBgMusicPlayer.isPlaying()) {
            trainingBgMusicPlayer.start();
        }
    }

    private void pausePlayer() {
        trainingBgMusicPlayer.pause();
    }


    private void releasePlayer() {
        // stop && release
        MusicPlayer.release(trainingBgMusicPlayer);

        musicPlayer.releaseAll();
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private void closeConfirm() {
        Intent intent = newIntent(TrainingGiveUpActivity.class);
        startActivityForResult(intent, CLOSE_CONFIRM_REQUEST_CODE);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
        if (CLOSE_CONFIRM_REQUEST_CODE == requestCode) {
            if (1 == resultCode) {
                stopTraining();
                finish();
            }
        }
    }
}



enum TrainingStatus {
    COUNTDOWN, COUNTDOWN_PAUSED,
    REST, REST_PAUSED,
    STARTED, PAUSED, STOPPED
}



class MusicManager {
    private static final String NUM_ONE = "/num_one.mp3";
    private static final String NUM_TWO = "/num_two.mp3";
    private static final String NUM_THREE = "/num_three.mp3";

    private static final String TRAINING_START = "/training_start.mp3";
    private static final String TRAINING_FINISH = "/training_finish.mp3";
    // 哨子声
    private static final String TRAINING_RECOVERY = "/training_recovery.mp3";


    private final String musicFileDir;


    public MusicManager(final String musicFileDir) {
        this.musicFileDir = musicFileDir;
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    public String getNumOne() {
        return musicFileDir + NUM_ONE;
    }

    public String getNumTwo() {
        return musicFileDir + NUM_TWO;
    }

    public String getNumThree() {
        return musicFileDir + NUM_THREE;
    }



    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -




    public String getTrainingStart() {
        return musicFileDir + TRAINING_START;
    }

    public String getTrainingRecovery() {
        return musicFileDir + TRAINING_RECOVERY;
    }

    public String getTrainingFinish() {
        return musicFileDir + TRAINING_FINISH;
    }



    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



    public void trainingStart(@NotNull TimerKit.Task callback) {
        play(getTrainingStart(), callback);
    }

    public void trainingRecovery(@NotNull TimerKit.Task callback) {
        play(getTrainingRecovery(), callback);
    }

    public void trainingFinish(@NotNull TimerKit.Task callback) {
        play(getTrainingFinish(), callback);
    }



    public void play(@NotNull final String music,
                     @NotNull TimerKit.Task callback) {
        final MediaPlayer mediaPlayer = new MediaPlayer();

        try {
            mediaPlayer.setDataSource(music);
            mediaPlayer.prepare();
            mediaPlayer.setLooping(false);
            mediaPlayer.setOnCompletionListener(mp -> {
                MusicPlayer.release(mediaPlayer);

                callback.run();
            });

            mediaPlayer.start();
        } catch (IOException e) {
            e.printStackTrace();

            MusicPlayer.release(mediaPlayer);
        }
    }

}


class MusicPlayer {
    private final MediaPlayer[] players;

    public MusicPlayer(@NotNull String...musics) {
        Log.i("MusicPlayer", "musics: " + Arrays.toString(musics));
        this.players = new MediaPlayer[musics.length];

        for (int i = 0; i < musics.length; i++) {
            final MediaPlayer temp = new MediaPlayer();
            temp.setLooping(false);
            try {
                temp.setDataSource(musics[i]);
                temp.prepare();
            } catch (IOException e) {
                e.printStackTrace();
            }

            players[i] = temp;
        }
    }



    public void start(@IntRange(from = 0) int index) {
        players[index].start();
    }

    public void release(@IntRange(from = 0) int index) {
        release(players[index]);
        // players[index] = null;
    }

    public void releaseAll() {
        for (MediaPlayer player : players) {
            release(player);
        }
    }

    public static void release(final MediaPlayer player) {
        if (null != player) {
            player.stop();
            player.reset();
            player.release();
        }
    }
}
